[AWS IoT Greengrass] awslabsで提供されている「AWS Greengrass Labs Certificate Rotator」を使用して証明書のローテーションを試してみました
1 はじめに
CX 事業本部 delivery部の平内(SIN)です。
AWS Greengrass Labs Certificate Rotator は、Amazon Web Services - Labsで公開されているリポジトリで、Greengrassのエッジデバイス上の証明書及び、秘密鍵をローテーションするソリューションです。
https://github.com/awslabs/aws-greengrass-labs-certificate-rotator
利用方法などについては、ブログや、Yotubeで詳細に解説されています。
How to manage AWS IoT Greengrass core device certificates
試してみたのですが、「非常に使いやすい」と感じたのと、「既に展開されているデバイスにも簡単に適用できそう」だったので、ここで、少し作業内容を紹介させてください。
なお、Greengrass nucleus の recipe変数 について、ちょっと戸惑ったところがあったので、そこについても触れたいと思います。
2 deploy
本ソリューションは、大きく以下の2つで構成されています。
1. Greengrassコンポーネント (AWSaws.greengrass.labs.certifaterotator) 2. CDKスタック * 主に3つのLambda関数 * 3つのAWS IoT Core Rule * カスタムジョブテンプレート(AWSLabScertificaterotator) * SNSトピックで構成
https://aws.amazon.com/jp/blogs/iot/how-to-manage-aws-iot-greengrass-core-device-certificates/
(1) clone
最初に、Githubのリポジトリをcloneして、ライブラリのインストールを行います。
% git clone git@github.com:awslabs/aws-greengrass-labs-certificate-rotator.git % cd aws-greengrass-labs-certificate-rotator/ % python3 -m venv venv % source venv/bin/activate (venv) % pip install -r requirements.txt
(2) CDK Backend
クラウド側のリソースは、cdkでdeployされます。
% cd backend % npm install % npm run build % cdk deploy
これにより、以下のリソースがデプロイされました。
Lambda
AWSLabsCertificateRotatorCustomResourceJobExecutionEvents AWSLabsCertificateRotatorCommitCertificate AWSLabsCertificateRotatorCreateCertificate AWSLabsCertificateRotatorJobExecutionTerminal
IoT TopicRule
AWSLabsCertificateRotatorCommitCertificate AWSLabsCertificateRotatorCreateCertificate AWSLabsCertificateRotatorJobExecutionTerminal
IoT JobTemplate
AWSLabsCertificateRotator
SNS Topic
AWSLabsCertificateRotatorNotification
(3) Greengrass Component
コンポーネントは、gdkでインストールします。
最初に、gdk-config.jsonの編集を行います。 変更が、必須なのは、ws.greengrass.labs.CertificateRotator.version とpublish.regionぐらいです。
gdk-config.json
{ "component": { "aws.greengrass.labs.CertificateRotator": { "author": "Amazon", "version": "1.0.0", "build": { "build_system": "custom", "custom_build_command": [ "python3", "gdk_build.py" ] }, "publish": { "bucket": "greengrass-certificate-rotator", "region": "ap-northeast-1" } } }, "gdk_version": "1.0.0" }
以下のコマンドで、greengrass-buildの下にartifacts及び、recipesが生成されます。
圧縮ファイル(certificate-rotator.zip)の元となっているのは、artifactsは以下のファイルですので、必要であれば、そこで編集できます。
gdk component build
% tree greengrass-build greengrass-build ├── artifacts │ └── aws.greengrass.labs.CertificateRotator │ └── 1.0.0 │ └── certificate-rotator.zip └── recipes └── recipe.yaml
続いて、S3へのアップロードとコンポーネントの作成を行います。
gdk component publish
使用されるバケットは、recipe.yamlで定義した下記となりますので、予めバケット作成とコアデバイスへのアクセス権付与は、必要になります。
greengrass-certificate-rotator-{region}-{account}
コンポーネントのデプロイは、以下のコマンドになります。 gg-group-0001は、デプロイ対象のグループです。
% python3 deploy_component_version.py 1.0.0 gg-group-0001
デプロイ後にローカルデバッグコンソール(aws.greengrass.LocalDebugConsole)で確認している様子です。
3 証明書・秘密鍵のローテイション
(1) IoT Job
クラウド側のリソース及び、コンポーネントのデプロイが終わったら、もう、ローテーションが可能です。
作業は、用意されたテンプレート(AWSLabsCertificateRotator)を基準にして、IoT Job で行います。
なお、テンプレートでは、スケジューリングや、再試行、中止の構成などは設定されていません。(自由に構成管理できます)
(2) 動作確認
ローテーション前のデバイス上の証明書・秘密鍵の状況です。
/greengrass/v2 $ ls -la ・・・略・・・ -rw------- 1 root root 1675 Jun 5 08:40 privKey.key -rw-r--r-- 1 root root 1188 Jun 5 02:34 rootCA.pem -rw-r--r-- 1 root root 1220 Jun 5 08:40 thingCert.crt $ openssl x509 -in thingCert.crt -noout -fingerprint -sha256 SHA256 Fingerprint=81:C2:FE:26:55:3E:B9:D5:95:F6:83:DC:BE:27:2D:AA:5C:3C:AD:F5:33:EB:FB:F0:6B:F2:44:9B:8B:1B:4A:53
ジョブを実行するとと、証明書・秘密鍵が変更されていることを確認できます。
/greengrass/v2 $ ls -la -rw------- 1 root root 1675 Jun 5 09:03 privKey.key -rw-r--r-- 1 root root 1188 Jun 5 02:34 rootCA.pem -rw-r--r-- 1 root root 1220 Jun 5 09:03 thingCert.crt $ openssl x509 -in thingCert.crt -noout -fingerprint -sha256 SHA256 Fingerprint=A9:D0:60:60:12:D2:69:09:34:D1:61:14:2D:0E:34:B4:19:49:77:66:6C:2D:4B:67:A4:08:6C:14:D4:8F:99:E9
4 interpolateComponentConfiguration
ここまでで、デプロイ及び、ローテーション作業は終わりで、ここからは、ちょっと私が躓いた内容です。
実は、ドキュメントの手順通り進めると、デプロイしたコンポーネントが、Subscribeに失敗している事が分かりました。
sudo tail -f /greengrass/v2/logs/aws.greengrass.labs.CertificateRotator.log
※ pubsub.pyの39行目、subscribeが失敗しています
stdout. Subscribing to topic $aws/things/gg-device-0001/jobs/notify-next. {scriptName=services.aws.greengrass.labs.CertificateRotator.lifecycle.Run.Script, serviceName=aws.greengrass.labs.CertificateRotator, currentState=RUNNING} stdout. Subscribe to topic stream closed.. {scriptName=services.aws.greengrass.labs.CertificateRotator.lifecycle.Run.Script, serviceName=aws.greengrass.labs.CertificateRotator, currentState=RUNNING} stderr. Traceback (most recent call last):. {scriptName=services.aws.greengrass.labs.CertificateRotator.lifecycle.Run.Script, serviceName=aws.greengrass.labs.CertificateRotator, currentState=RUNNING} stderr. File "/greengrass/v2/packages/artifacts-unarchived/aws.greengrass.labs.CertificateRotator/1.0.6/certificate-rotator/pubsub.py", line 39, in subscribe. {scriptName=services.aws.greengrass.labs.CertificateRotator.lifecycle.Run.Script, serviceName=aws.greengrass.labs.CertificateRotator, currentState=RUNNING}
確認したところ、こちらは、recipe.yamlで使用されているrecipe変数 ({iot:thingName}) が、正常に展開されていない事が原因であると分かりました。
aws.greengrass.ipc.mqttproxy: aws.greengrass.labs.CertificateRotator:mqttproxy:1: policyDescription: Allows access to publish to relevant topics operations: - "aws.greengrass#PublishToIoTCore" resources: - "$aws/things/{iot:thingName}/jobs/+/get" - "$aws/things/{iot:thingName}/jobs/+/update"
このrecipe変数は、Greengrass nucleus コンポーネント v2.6.0以降で利用可能になっているのですが、使用するためには、オプションのinterpolateComponentConfigurationがtureになっている必要がありました。
確認したところ、依存関係でインストールされたaws.greengrass.Nucleusは、デフォルトのfalseで動作していました。
ここをtureに変更する事で、コンポーネントのエラーは発生しなくなりました。
参考:{iot:thingName} recipe variable not working #1366
参考:interpolateComponentConfiguration
5 最後に
IoTソリューションを安全に運用するためのセキュリティアプローチとして、デバイス証明書と秘密鍵の定期的なローテーションや、ライフサイクルの管理は、セキュリティベストプラクティスとなっています。
しかし、一般的にエッジ側に分散された多数のデバイスの証明書を更新するのは、結構やっかいな問題となります。
AWS IoT Greengrassでは、証明書と秘密鍵の場所は、certificateFilePath および PrivateKeypath 構成パラメーターによって定義されていて、その対象が明らかなので、今回のようなソリューションが可能になっているのだと感じました。
今回試したのは、単純なローテーションだけでしたが、このソリューションは、まだまだ柔軟に利用が可能になっています。逐次、確認を進めたいと思います。